#include <Arduino.h>
#include <WiFi.h>
#include "esp_camera.h"
#include "UdpClient.hpp"
#include "base64.h"
#include "UrlEncode.h"
#include <HTTPClient.h>
#include <ArduinoJson.h> // Arduino的JSON库

#ifndef OVER_LOAD_CAM_CONF
#define CAMERA_MODEL_AI_THINKER // arduino ide 编译时释放该项选择esp32cam摄像头引脚定义
#define FRAMESIZE FRAMESIZE_VGA
//#define HE_ZHOU_S3 // arduino ide 编译时释放该项选择合宙S3摄像头引脚定义
//#define CAMERA_MODEL_WROVER_KIT

#endif

#include "cam_pins.h"
volatile bool buttonPressed2 = false; // 按键2中断标志
const int buttonPin2 = 2;
const char *ssid = "IOTNET";
const char *passwd = "12345678";
const char *host = "192.168.137.1";
const char* client_id = "vVMDbmL9a7IgJez5MJuuSeFe";
const char* client_secret = "YXwaZpoY6gHuCpnoW1zD2j6PG1OulRtc";

const uint16_t serverUdpPort = 8004;
const uint16_t serverUdpPort2 = 8848;

/*
   获取通道索引的端口(tcp)
   当然也可以使用发送图片的UDP客户端获取，但必须保证获取到之后才能发送图片帧，需要反复判断重传和双向ack
   故在启动时通过TCP获取通道索引
  */
const uint16_t channelBindPort = 8004;
const uint16_t  localTCPort = 8848;

const uint16_t localUdpPort = 2333;
const uint16_t localUdpPort2 = 2334;
LightUDP streamSender;

// 绑定sn后续为mac 作为sn  代表后续内容是SN 后一位是 6表示SN长度 空位的0 将填充mac

int channel_index = -1;
int test_index=0;
const int adcPin = 12; // ADC引脚，可以根据需要更改为其他引脚


char *img_access_token = "24.84a8228fdbb0c2996bf15340340e9ee5.2592000.1718724386.282335-72467245";


String revBase64Reslut;
String url_buf;
void connectWifi(const char *ssid, const char *passphrase) {
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, passphrase);

    Serial.println("connecting to router... ");
    //等待wifi连接成功
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(".");
        delay(500);
    }
    Serial.print("\nWiFi connected, local IP address:");
    Serial.println(WiFi.localIP());
}
void IRAM_ATTR buttonISR2() {
  buttonPressed2 = true; // 设置按键2中断标志
}
String getAccessToken() {
  // 使用HTTPClient发送请求获取access_token
  HTTPClient http;
  http.begin("https://aip.baidubce.com/oauth/2.0/token");
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");
  
  String body = "grant_type=client_credentials&client_id=" + String(client_id) + "&client_secret=" + String(client_secret);
  
  int httpCode = http.POST(body);
  if (httpCode == HTTP_CODE_OK) {
    String payload = http.getString();
    StaticJsonDocument<600> jsonDoc;
    deserializeJson(jsonDoc, payload);
    return jsonDoc["access_token"]; // 返回access_token
  }
  return "";
}

 



void setup() {
    Serial.begin(115200);
    Serial.setDebugOutput(true);

   // Serial.println(str1)
    
    while (!Serial) {
        /* code */
    }

    pinMode(buttonPin2, INPUT);   // 开启引脚输出模式（有个别引脚不可以设置输出模式，在文章后面会提到）
    digitalWrite(buttonPin2,LOW);

    pinMode(4, OUTPUT);   //上拉输入模式，INPUT就是不上拉也不下拉，下拉就是INPUT_PULLDOWN,有些引脚内部已经上下拉了的，在后面我提到了
    digitalWrite(4,LOW);

    analogReadResolution(12); // 设置 ADC 分辨率为 12 位

    attachInterrupt(digitalPinToInterrupt(buttonPin2), buttonISR2, RISING); // 将按键2中断函数与引脚的下降沿触发关联
    

    camera_config_t config;
    config.ledc_channel = LEDC_CHANNEL_0;
    config.ledc_timer = LEDC_TIMER_0;
    config.pin_d0 = Y2_GPIO_NUM;
    config.pin_d1 = Y3_GPIO_NUM;
    config.pin_d2 = Y4_GPIO_NUM;
    config.pin_d3 = Y5_GPIO_NUM;
    config.pin_d4 = Y6_GPIO_NUM;
    config.pin_d5 = Y7_GPIO_NUM;
    config.pin_d6 = Y8_GPIO_NUM;
    config.pin_d7 = Y9_GPIO_NUM;
    config.pin_xclk = XCLK_GPIO_NUM;
    config.pin_pclk = PCLK_GPIO_NUM;
    config.pin_vsync = VSYNC_GPIO_NUM;
    config.pin_href = HREF_GPIO_NUM;
    config.pin_sccb_sda = SIOD_GPIO_NUM;
    config.pin_sccb_scl = SIOC_GPIO_NUM;
    config.pin_pwdn = PWDN_GPIO_NUM;
    config.pin_reset = RESET_GPIO_NUM;
    config.xclk_freq_hz = 20000000;
    config.pixel_format = PIXFORMAT_JPEG;
    config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
    config.fb_location = CAMERA_FB_IN_PSRAM;
    config.jpeg_quality = 12;
    config.fb_count = 1;

    if (psramFound()) {
        config.frame_size = FRAMESIZE_XGA;
        config.jpeg_quality = 10;
        config.fb_count = 2;
        config.grab_mode = CAMERA_GRAB_LATEST;
    } else {
        // 使用直接内存申请
        config.frame_size = FRAMESIZE_SVGA;
        config.fb_location = CAMERA_FB_IN_DRAM;
    }


    // camera init
    esp_err_t err = esp_camera_init(&config);
    if (err != ESP_OK) {
        log_i("Camera init failed with error 0x%x", err);
        return;
    }
    log_i("get sensor ");
    sensor_t *s = esp_camera_sensor_get();
    s->set_framesize(s, FRAMESIZE);

    connectWifi(ssid, passwd);
    WiFiClient client;

    uint8_t bind_sn[] = {'S', 'N', ':', 6, 0, 0, 0, 0, 0, 0};
    uint8_t TCP_send[]={'p' , 'e' , 'r' , 's' , 'o' , 'n' , ':' , 1};
    uint8_t TCP_send_N[]={ 'p' , 'e' ,  'r' , 's' , 'o' , 'n' , ':' , 0 };

     //将mac设置到 bind_sn 第3位
    WiFi.macAddress(&bind_sn[4]);
    Serial.printf("MAC is:%02X%02X%02X%02X%02X%02X\n", bind_sn[4], bind_sn[5],
                 bind_sn[6], bind_sn[7], bind_sn[8], bind_sn[9]);
    int index;
    do {
        while (!client.connect(host, channelBindPort)) {
            log_i("connection failed,wait 3 sec...");
            delay(3000);
        }
        client.setNoDelay(true);
        client.write(bind_sn, 10);
         client.flush();
        delay(3000);
      index = client.read();
   } 
    while (index == -1);

    if (index <= 255) {
        channel_index = index;
        log_i("channel_index is : %d ", channel_index);
    }
    client.stop();


    streamSender.begin(WiFi.localIP(), localUdpPort);
    streamSender.setServer(host, serverUdpPort);


}


void loop() {

   
    return_:
    Serial.println("do loop");
    if (channel_index == -1) {
        log_i("delay 3 sec");
        delay(3000);
        goto return_;
   }
    camera_fb_t *fb = NULL;
    size_t len;
    log_i("send image");

   while (true) {

        fb = esp_camera_fb_get();
       if (!fb) {
          Serial.println("Camera capture failed");
           break;
        }
        len = fb->len;
        fb->buf[len] = channel_index;
        streamSender.send(fb->buf, len + 1);
            if (buttonPressed2) {
          
           revBase64Reslut= base64::encode(fb->buf,len);
           url_buf= urlEncode(revBase64Reslut);
         
               Serial.println("Url:");

              // Serial.println(url_buf);
               Serial.println("End");
     String accessToken = getAccessToken();
  if (accessToken != "") {
     

  HTTPClient http;
  http.begin("https://aip.baidubce.com/rest/2.0/image-classify/v1/body_num?access_token=" + accessToken);
  http.addHeader("Content-Type", "application/json");

  //StaticJsonDocument<1280> jsonDoc;
  //jsonDoc["image"] =url_buf;
  //String jsonPayload;
  //serializeJson(jsonDoc, jsonPayload);
 String body = "image=" +url_buf;
  
  int httpCode = http.POST(body);
  if (httpCode == HTTP_CODE_OK) {
    String payload = http.getString();
   StaticJsonDocument<200> jsonDoc;
  // 解析JSON字符串
  deserializeJson(jsonDoc, payload);
  // 提取person_num对应的值
  int personNum = jsonDoc["person_num"]; // 默认类型为int

  // 打印提取的值
  Serial.println("person_num: ");
  Serial.println(personNum);
  } else {
    Serial.println("Error on HTTP request");
  }
  } else {
    Serial.println("Failed to get access token.");
  }
   

                   digitalWrite(4,HIGH);
                  // Serial.println(revBase64Reslut);      //发送给ESP32主控
                   buttonPressed2 = false; // 重置按键2中断标志
          }
          esp_camera_fb_return(fb);
          digitalWrite(4,LOW);
          
        }
        
        
  }
  

